package formula.business.dao;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import twork.util.basic.ArrayWithTotalList;
import formula.business.model.Driver;
import formula.business.model.Level;
import formula.business.pojo.DriverFilter;

@Repository("driverDao")
@Transactional
public class DriverDaoJpaImpl implements DriverDao {

	@PersistenceContext
	private EntityManager em;

	@Override
	public Driver save(Driver _driver) {
		em.persist(_driver);
		return _driver;
	}

	@Override
	public void delete(Driver _driver) {
		Driver dManaged = em.merge(_driver);
		em.remove(dManaged);
	}

	@Override
	public Driver deleteDiver(Long _id) {
		Driver driver = em.find(Driver.class, _id);
		// Query query = em.createQuery("delete from Driver p where p.id = :id");
		// query.setParameter("id", _id);
		// query.executeUpdate();
		em.remove(driver);
		return driver;
	}

	@Override
	public <T> List<T> selectAll(Class<T> c) {
		return em.createQuery("from " + c.getName(), c).getResultList();
	}

	@Override
	public List<Level> selectLevel() {
		List<Level> listLevel = em.createQuery("from Level", Level.class).getResultList();
		return listLevel;
	}

	@Override
	public Driver merge(Driver _driver) {
		Driver p = em.merge(_driver);
		return p;
	}

	@Override
	public ArrayWithTotalList<Driver> searchAndCountDrivers(DriverFilter _filter) {
		Query queryCount = buildQueryDrivers(_filter, true);
		int count = Integer.valueOf("" + queryCount.getSingleResult());
		Query querySelect = buildQueryDrivers(_filter, false);
		@SuppressWarnings("unchecked")
		List<Driver> list = querySelect.getResultList();
		ArrayWithTotalList<Driver> driversWithCount = new ArrayWithTotalList<Driver>(list);
		driversWithCount.setTotalCount(count);
		return driversWithCount;
	}

	@Override
	public int searchCountDrivers(DriverFilter _filter) {
		Query queryCount = buildQueryDrivers(_filter, true);
		int count = Integer.valueOf("" + queryCount.getSingleResult());
		return count;
	}

	@Override
	public List<Driver> searchDrivers(DriverFilter _filter) {
		Query querySelect = buildQueryDrivers(_filter, false);
		@SuppressWarnings("unchecked")
		List<Driver> list = querySelect.getResultList();
		return list;
	}

	private Query buildQueryDrivers(DriverFilter _filter, boolean _count) {
		StringBuilder queryString = null;
		if (_count) {
			// no "fetch" Here
			queryString = new StringBuilder("select count(p) from Driver as p left join p.level as l "
					+ " left join p.team as team " + "WHERE  1=1 ");
		} else {
			// with "fetch" Here
			queryString = new StringBuilder("from Driver as p left join fetch p.level as l "
					+ " left join fetch p.team as team " + "WHERE  1=1 ");
		}
		if (_filter == null) {
			queryString.append(" order by p.id asc");
			Query query = em.createQuery(queryString.toString());
			return query;
		}
		//
		HashMap<String, Object> map = new HashMap<String, Object>();
		//
		String lastname = _filter.getLastname();
		boolean isLastName = lastname != null && lastname.trim().length() > 0;
		if (isLastName) {
			queryString.append(" AND p.lastname LIKE :lastname ");
			map.put("lastname", lastname);
		}
		String fisrtname = _filter.getFirstname();
		boolean isFisrtName = fisrtname != null && fisrtname.trim().length() > 0;
		if (isFisrtName) {
			queryString.append(" AND p.firstname LIKE :firstname ");
			map.put("firstname", fisrtname);
		}
		//
		boolean isWithLevel = _filter.getLevelId() != null;
		if (isWithLevel) {
			queryString.append(" AND l.id=:levelid ");
			map.put("levelid", _filter.getLevelId());
		}
		//
		boolean isAged = _filter.getAge() != null && _filter.getAge() > 0;
		if (isAged) {
			queryString.append(" AND p.age=:age ");
			map.put("age", _filter.getAge());
		}
		//
		boolean isTeamed = _filter.getTeamId() != null;
		if (isTeamed) {
			queryString.append(" AND team.id= :teamId ");
			map.put("teamId", _filter.getTeamId());
		}
		if (!_count) {
			Boolean sortAscending = _filter.getSortAscending();
			String sortColumnName = _filter.getSortColumnName();
			if (sortColumnName.length() > 0) {
				queryString.append(" order by p." + sortColumnName + " " + (sortAscending ? "asc" : "desc"));
			}
		}
		Query query = em.createQuery(queryString.toString());
		for (Iterator<String> iterator = map.keySet().iterator(); iterator.hasNext();) {
			String key = iterator.next();
			query.setParameter(key, map.get(key));
		}
		// In this case, "pure" Hibernate is faster ! => query.setsetProperties(map);
		//
		if (!_count) {
			query.setFirstResult(_filter.getFirstResult());
			query.setMaxResults(_filter.getPageSize());
		}
		return query;
	}

	@Override
	public List<Driver> selectDrivers() {
		List<Driver> list = em.createQuery(
				"from Driver as p left join fetch p.level left join fetch p.team order by p.id asc", Driver.class)
				.getResultList();
		return list;
	}

	@Override
	public Driver selectDriverById(long id) {
		return em
				.createQuery("from Driver as p left join fetch p.level left join fetch p.team " + " WHERE p.id=:id",
						Driver.class).setParameter("id", id).getSingleResult();
	}
}
